/*
 * main.c
 *
 *  Created on: Jul 27, 2013
 *      Author: derekmorris
 */

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OpenGL ES 2.0 code



#include <engine.h>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
	GLint error;
    for (error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

static const char gVertexShader[] =
	"uniform mat4 u_mvpMatrix;\n"
    "attribute vec3 a_position;\n"
	"attribute vec4 a_color;\n"
	"varying vec4 v_color;\n"
    "void main() {\n"
    //"gl_Position = vec4(a_position, 1);\n"
	"gl_Position = u_mvpMatrix * vec4(a_position, 1);\n"
	"  v_color = a_color;\n"
    "}\n";

static const char gFragmentShader[] =
    "precision mediump float;\n"
	"varying vec4 v_color;\n"
    "void main() {\n"
    //"  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
	"  gl_FragColor = v_color;\n"
    "}\n";

GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }

    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }

    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}

uint gProgram;
uint gvPositionHandle;
uint gvColorHandle;
uint gvMVPHandle;

Matrix4x4 gmatWorld;
float gWorldRot = 0.0f;
float gWorldRot2 = 0.0f;
Matrix4x4 gmatProjection;

bool setupGraphics(int w, int h) {
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
    gProgram = createProgram(gVertexShader, gFragmentShader);
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }

    gvPositionHandle = glGetAttribLocation(gProgram, "a_position");
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"a_position\") = %d\n", gvPositionHandle);

    gvColorHandle = glGetAttribLocation(gProgram, "a_color");
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"a_color\") = %d\n", gvColorHandle);

    gvMVPHandle = glGetUniformLocation(gProgram, "u_mvpMatrix");
    checkGlError("glGetUniformLocation");
    LOGI("glGetUniformLocation(\"u_mvpMatrix\") = %d\n", gvMVPHandle);

    glViewport(0, 0, w, h);
    checkGlError("glViewport");

    matrixPerspective(&gmatProjection, 50.0f, 1920.0f / 1080.0f, 0.01f, 1000.0f);

    return true;
}

void renderFrame() {
    static float grey = 0.0f;
   //grey += 0.01f;
    if (grey > 1.0f) {
        grey = 0.0f;
    }
    glClearColor(grey, grey, grey, 1.0f);
    checkGlError("glClearColor");
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    checkGlError("glClear");

    glUseProgram(gProgram);
    checkGlError("glUseProgram");


    glUniformMatrix4fv(gvMVPHandle, 1, false, gmatProjection.elements);


    Vector3 v0 = {-0.75f, 1.0f, -5.0f};
    Vector3 v1 = {-1.0f, 0.5f, -5.0f};
    Vector3 v2 = {-0.5f, 0.5f, -5.0f};
    Vector4 color = {1.0f, 0.0f, 0.0f, 1.0f};
    drawTriangle(&v0, &v1, &v2, &color, gvPositionHandle, gvColorHandle);

    Vector3 v0_1 = {0.75f, -0.5f, -10.0f};
    Vector3 v1_1 = {1.0f, -1.0f, -10.0f};
    Vector3 v2_1 = {0.5f, -1.0f, -10.0f};

    static float adj = 0.0f;
    adj += 0.01f;
    if (adj > 2.0f) adj = 0.0f;
    v0_1.y += adj;
    v1_1.y += adj;
    v2_1.y += adj;

    Vector4 color_1 = {0.0f, 0.0f, 1.0f, 1.0f};
    drawTriangle(&v0_1, &v1_1, &v2_1, &color_1, gvPositionHandle, gvColorHandle);

    gWorldRot -= 2.0f;
    matrixRotate(&gmatWorld, gWorldRot, 0.0f, 1.0f, 0.0f);
    gmatWorld.elements[14] = -5.0f;
    Matrix4x4 matWVP;
    matrixMultiply(&matWVP, &gmatWorld, &gmatProjection);
    glUniformMatrix4fv(gvMVPHandle, 1, false, matWVP.elements);
    drawCube(&gmatWorld, gvPositionHandle, gvColorHandle);


    gWorldRot2 += 2.0f;
	matrixRotate(&gmatWorld, gWorldRot2, 1.0f, 0.0f, 0.0f);
	gmatWorld.elements[12] = 3.0f;
	gmatWorld.elements[14] = -10.0f;
	matrixMultiply(&matWVP, &gmatWorld, &gmatProjection);
	glUniformMatrix4fv(gvMVPHandle, 1, false, matWVP.elements);
	drawCube(&gmatWorld, gvPositionHandle, gvColorHandle);
}


JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height);
JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj);

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
{
    setupGraphics(width, height);
}

JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj)
{
    renderFrame();
}

